﻿/* Copyright (C) 2007-2010 STS Soft

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; version 2 of the License.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

using System;
using System.Collections.Generic;
using System.IO;
using STSdb.General.Buffers;
using STSdb.General.Compression;

namespace STSdb.Data
{
    [Serializable]
    public class Locator : IComparable<Locator>, IEquatable<Locator>
    {
        private int hashCode = 0;

        public const char SEPARATOR = '\\';
        public readonly string[] Items;
        public static readonly Locator Empty = new Locator(String.Empty);

        private Locator(int capacity)
        {
            if (capacity <= 0)
                throw new ArgumentException("capacity");

            Items = new string[capacity];
        }

        public Locator(string[] items, int index, int count)
            : this(count)
        {
            Array.Copy(items, index, Items, 0, count);
        }

        public Locator(params string[] items)
            : this(items, 0, items.Length)
        {
        }

        public Locator(string locator)
            : this(locator.Split(SEPARATOR))
        {
        }

        #region Serialize/Deserialize

        public void Serialize(BinaryWriter writer)
        {
            CountCompression.Serialize(writer, Items.Length);
            for (int i = 0; i < Items.Length; i++)
                writer.Write(Items[i]);
        }

        public static Locator Deserialize(BinaryReader reader)
        {
            Locator locator = new Locator((int)CountCompression.Deserialize(reader));
            for (int i = 0; i < locator.Items.Length; i++)
                locator.Items[i] = reader.ReadString();

            return locator;
        }

        #endregion

        public override int GetHashCode()
        {
            if (hashCode == 0)
            {
                HashCodeBuilder builder = new HashCodeBuilder();
                for (int i = 0; i < Items.Length; i++)
                    builder.Append(Items[i]);

                hashCode = builder.GetHashCode();
            }

            return hashCode;
        }

        public override bool Equals(object obj)
        {
            if (!(obj is Locator))
                return false;

            return Equals((Locator)obj);
        }

        public override string ToString()
        {
            return string.Join(SEPARATOR.ToString(), Items);
        }

        public static bool operator ==(Locator x, Locator y)
        {
            if (Object.ReferenceEquals(x, y))
                return true;

            if (Object.ReferenceEquals(x, null))
                return false;

            return x.Equals(y);
        }

        public static bool operator !=(Locator x, Locator y)
        {
            return !(x == y);
        }

        public static implicit operator string(Locator locator)
        {
            return locator.ToString();
        }

        #region IComparable<Locator> Members

        public int CompareTo(Locator other)
        {
            if (Object.ReferenceEquals(other, null))
                throw new ArgumentNullException("other");

            int minLength = Math.Min(Items.Length, other.Items.Length);
            for (int i = 0; i < minLength; i++)
            {
                int cmp = Items[i].CompareTo(other.Items[i]);
                //cmp = string.CompareOrdinal(Items[i], other.Items[i]);
                if (cmp != 0)
                    return cmp;
            }

            return Items.Length.CompareTo(other.Items.Length);
        }

        #endregion

        #region IEquatable<Locator> Members

        public bool Equals(Locator other)
        {
            if (Object.ReferenceEquals(other, null))
                return false;

            if (Items.Length != other.Items.Length)
                return false;

            for (int i = 0; i < Items.Length; i++)
                if (Items[i] != other.Items[i])
                    return false;

            return true;
        }

        #endregion

        public string this[int index]
        {
            get { return Items[index]; }
        }

        public int Length
        {
            get { return Items.Length; }
        }

        public Locator Replace(int index, string item)
        {
            Locator locator = new Locator(Items);
            locator.Items[index] = item;
            return locator;
        }

        public Locator RemoveAt(int index)
        {
            List<string> items = new List<string>(Items);
            items.RemoveAt(index);
            return new Locator(items.ToArray());
        }

        public Locator Append(params string[] items)
        {
            Locator locator = new Locator(this.Items.Length + items.Length);

            for (int i = 0; i < this.Items.Length; i++)
                locator.Items[i] = this.Items[i];

            for (int i = 0; i < items.Length; i++)
                locator.Items[this.Items.Length + i] = items[i];

            return locator;
        }

        public bool StartsWith(Locator locator)
        {
            if (Length < locator.Length)
                return false;

            for (int i = 0; i < locator.Length; i++)
            {
                if (this[i] != locator[i])
                    return false;
            }

            return true;
        }
    }
}
